/**
 * 
 */
package edu.arizona.bio5.onto.decomposition.persistent.xml;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.io.OWLXMLOntologyFormat;
import org.semanticweb.owlapi.io.StringDocumentTarget;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.w3c.dom.Document;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Database;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.CollectionManagementService;
import org.xmldb.api.modules.XMLResource;

import edu.arizona.bio5.onto.decomposition.Atom;
import edu.arizona.bio5.onto.decomposition.AtomicDecomposition;
import edu.arizona.bio5.onto.decomposition.persistent.ADPersistenceException;

/**
 * Stores the atomic decomposition into an XML databases using XML:DB API
 * 
 * @author Pavel Klinov
 *
 */
public class XMLDBPersister extends XMLADPersister {

	private final String mDatabaseURI;//"xmldb:exist://localhost:8080/xmlrpc";
	private final String mOntologyName;
	private final Database mDB;
	private Collection mRootCollection;
	private final String mUser;
	private final String mPwd;
	private OWLOntology mOntology = null;
	
	public XMLDBPersister(final String dbURI, final String ontoName, final String username, final String password) throws ADPersistenceException {
		mDatabaseURI = dbURI;
		mOntologyName = ontoName;
		mUser = username;
		mPwd = password;
		
		final String driver = "org.exist.xmldb.DatabaseImpl";
		// initialize database driver
		try {
			Class<?> cl = Class.forName(driver);
			
			mDB = (Database) cl.newInstance();
			//database.setProperty("create-database", "true");
			DatabaseManager.registerDatabase(mDB);
			
		} catch (Throwable e) {
			throw !(e instanceof ADPersistenceException) ? new ADPersistenceException(e) : (ADPersistenceException)e;
		}
	}
	
	public XMLDBPersister(final String dbURI, final String ontoName) throws ADPersistenceException {
		this(dbURI, ontoName, "guest", "");
	}
	
	public void setOntology(OWLOntology ontology) {
		mOntology = ontology;
	}
	
	private void assertRootCollection() throws ADPersistenceException {
		if (mRootCollection == null) {
			throw new ADPersistenceException("Can't get the root collection of the XML DB");
		}
		/*else {
			try {
				for (String child : mRootCollection.listChildCollections()) {
					System.out.println(child);
				}
			} catch (XMLDBException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}*/
	}
	
	@Override
	protected void prepare() throws ADPersistenceException {
		try {
			mRootCollection = DatabaseManager.getCollection(mDatabaseURI, mUser, mPwd);
			
			assertRootCollection();
			
			System.out.print("Cleaning up XML DB... ");
			
			//Get the root AD XML collection
			Collection adColl = mRootCollection.getChildCollection(mOntologyName);
			Collection atomColl = null;
			CollectionManagementService mgt = (CollectionManagementService) mRootCollection.getService("CollectionManagementService", "1.0");
			
			if (adColl != null) {//Found,  nuke it
				mgt.removeCollection(adColl.getName());
			}
			//Now recreate
			mRootCollection = mgt.createCollection(mOntologyName);
			atomColl = mgt.createCollection(mOntologyName + "/" + "atoms");
			
			atomColl.close();
			//Set the original ontology, if provided
			saveOntology();
			
			System.out.println("done!");
			
		} catch (XMLDBException e) {
			//Can only die...
			throw new ADPersistenceException(e);
		}
	}

	private void saveOntology() throws ADPersistenceException {
		try {
			if (mOntology != null) {
				OWLOntologyManager mgr = OWLManager.createOWLOntologyManager();
				StringDocumentTarget target = new StringDocumentTarget();
				
				
				mgr.saveOntology(mOntology, new OWLXMLOntologyFormat(), target);
				persistString(target.toString(), XMLADConstants.SOURCE_ONTOLOGY_DOC);
			}
		} catch (Exception e) {
			throw new ADPersistenceException(e);
		}
	}

	protected Collection getDocumentCollection(String docName) throws ADPersistenceException, XMLDBException {
		assertRootCollection();

		Collection docColl = mRootCollection;
		int index = docName.lastIndexOf('/');

		if (index > 0) {
			String parent = docName.substring(0, index);

			docColl = docColl.getChildCollection(parent);
			docName = docName.substring(index + 1);

		}

		return docColl;
	}
	
	protected void closeDocumentCollection(Collection docColl) throws XMLDBException {
		if (docColl != mRootCollection) {
			docColl.close();
		}
	}
	
	@Override
	protected void persistDocument(Document doc, String docName) throws ADPersistenceException {
		assertRootCollection();
		
		try {
			Collection docColl = getDocumentCollection(docName);
			XMLResource docResource = (XMLResource)docColl.createResource(docName, "XMLResource");
			
			docResource.setContentAsDOM(doc);
			docColl.storeResource(docResource);
			
			closeDocumentCollection(docColl);
		} catch (XMLDBException e) {
			throw new ADPersistenceException(e);
		}
	}


	protected void persistString(String str, String docName) throws ADPersistenceException {
		assertRootCollection();
		
		try {
			Collection docColl = getDocumentCollection(docName);
			XMLResource docResource = (XMLResource)docColl.createResource(docName, "XMLResource");
			
			docResource.setContent(str);
			docColl.storeResource(docResource);
			
			closeDocumentCollection(docColl);
		} catch (XMLDBException e) {
			throw new ADPersistenceException(e);
		}
	}
	
	
	@Override
	protected void persistAtoms(AtomicDecomposition ad) throws ADPersistenceException {
		for (Atom atom : ad.getAtoms()) {
			persistString(getAtomAsString(ad.getOntologyURI().toString(), atom), "atoms/" + XMLADConstants.ATOM_DOC_PREFIX + atom.getID() + ".owl");
		}
	}

	protected String getAtomAsString(String ontoURI, Atom atom) throws ADPersistenceException {
		
		OWLOntologyManager mgr = OWLManager.createOWLOntologyManager();
		StringDocumentTarget target = new StringDocumentTarget();
		
		try {
			OWLOntology ontology = mgr.createOntology(atom.getAxioms(), IRI.create(ontoURI + "/" + XMLADConstants.ATOM_DOC_PREFIX + atom.getID() + ".owl"));
			
			mgr.saveOntology(ontology, new OWLXMLOntologyFormat(), target);
			
			return target.toString();
			
		} catch (Throwable e) {
			throw new ADPersistenceException(e);
		}
	}	
	
	
	@Override
	protected void finish() {
		try {
			if (mRootCollection != null) {
				mRootCollection.close();
				mRootCollection = null;
			}
		} catch (XMLDBException e) {
			// Fuck it
		} 
	}
}